#pragma once

#include "types.h"
#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

  typedef int ca_iter;
  typedef uint32_t usize_t;
  typedef void* CADATAPTR;

  typedef struct CircleArray
  {
    int full;
    ca_iter s;
    ca_iter e;
    ca_iter c;
    usize_t siz;
    usize_t item_size;
    usize_t data_size;
    CADATAPTR data_ptr;
  } CircleArray;

#ifndef CIRCLE_ARRAY_END
#define CIRCLE_ARRAY_END
#define ca_iter_end -1
#endif // CIRCLE_ARRAY_END

  void init_circle_array(CircleArray* ca,
                         CADATAPTR data,
                         usize_t item_size,
                         usize_t array_size);

  CircleArray* create_circle_array(const usize_t item_size,
                                   const usize_t array_size);
  void release_circle_array(CircleArray** ca);
  CADATAPTR ca_get_data_ptr(const CircleArray* ca);
  CADATAPTR ca_get_data(const CircleArray* ca, const ca_iter it);
  ca_iter ca_front(const CircleArray* ca);
  ca_iter ca_back(const CircleArray* ca);
  ca_iter ca_current(const CircleArray* ca);
  ca_iter ca_next(const CircleArray* ca, const ca_iter it);
  ca_iter ca_prev(const CircleArray* ca, const ca_iter it);
  ca_iter ca_next_(const CircleArray* ca, const ca_iter it);
  ca_iter ca_prev_(const CircleArray* ca, const ca_iter it);
  ca_iter ca_new_data(CircleArray* ca, const CADATAPTR data);

  ca_iter ca_pop_back(CircleArray* ca);
  ca_iter ca_pop_front(CircleArray* ca);
  ca_iter ca_delete(CircleArray* ca, const ca_iter it);

  CADATAPTR ca_data(const CircleArray* ca, const ca_iter it);
  int ca_remove(CircleArray* ca, const ca_iter it);

  void ca_clear(CircleArray* ca);

#define CA_GET_DATA(Type, ca, it) ((Type*)(ca_get_data(ca, it)))

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus

template<typename T, usize_t deep>
struct CircleVector
{
  CircleVector() noexcept
  {
    if (deep)
      deep_ = deep;
    ca = (CircleArray*)(create_circle_array(sizeof(T), deep_));
  }

  ~CircleVector() noexcept { release_circle_array(&ca); }

  T* get_data_ptr() const noexcept { return ca->data_ptr; }

  T* get_data(const ca_iter it) { return (T*)(ca_get_data(ca, it)); }
  T& get(const ca_iter it) { return (*(T*)(ca_get_data(ca, it))); }

  ca_iter it_front() const noexcept { return ca_front(ca); }
  ca_iter it_back() const noexcept { return ca_back(ca); }
  ca_iter it_current() const noexcept { return ca_current(ca); }
  ca_iter it_next(const ca_iter it) const noexcept { return ca_next(ca, it); }
  ca_iter it_prev(const ca_iter it) const noexcept { return ca_prev(ca, it); }
  ca_iter it_next_(const ca_iter it) const noexcept { return ca_next_(ca, it); }
  ca_iter it_prev_(const ca_iter it) const noexcept { return ca_prev_(ca, it); }

  T* front() const noexcept
  {
    auto it = ca_front(ca);
    return (it == ca_iter_end) ? nullptr : ((T*)(ca_get_data(ca, it)));
  }

  T* back() const noexcept
  {
    auto it = ca_back(ca);
    return (it == ca_iter_end) ? nullptr : ((T*)(ca_get_data(ca, it)));
  }

  T* current() const noexcept
  {
    auto it = ca_current(ca);
    return (it == ca_iter_end) ? nullptr : ((T*)(ca_get_data(ca, it)));
  }

  ca_iter new_data(const T* new_data) noexcept
  {
    return ca_new_data(ca, (const CADATAPTR)(new_data));
  }

  ca_iter new_data(const T& new_data) noexcept
  {
    return ca_new_data(ca, (const CADATAPTR)(&new_data));
  }

  void clear() noexcept { return ca_clear(ca); }

  ca_iter pop_back() noexcept { return ca_pop_back(ca); }
  ca_iter pop_front() noexcept { return ca_pop_front(ca); }

  ca_iter del(const ca_iter it) noexcept { return ca_delete(ca, it); }

  T* data(const ca_iter it) const noexcept { return (T*)(ca_data(ca, it)); }
  T& the_data(const ca_iter it) const noexcept
  {
    return *(T*)(ca_data(ca, it));
  }

  int remove(const int index) noexcept { return ca_remove(ca, index); }

private:
  usize_t deep_ = 100;
  CircleArray* ca = 0;
};

#include <iostream>
#include <set>

// ////////////////////////////////////////////////////////////////////////////
//
// SetPointer
//
// ////////////////////////////////////////////////////////////////////////////
template<typename T>
struct Comparator
{
  bool operator()(const T* a, const T* b) const { return (*a < *b); }
};

template<typename T, usize_t deep>
class PointerSet : public std::set<T*, Comparator<T>>
{
public:
  PointerSet() noexcept
    : deep_(deep)
  {
  }
  virtual ~PointerSet() { set_clear(); }

  void set_clear() noexcept
  {
    for (auto&& it = std::set<T*, Comparator<T>>::begin();
         it != std::set<T*, Comparator<T>>::end();
         ++it)
      if ((*it) != nullptr)
        delete (*it);
    std::set<T*, Comparator<T>>::clear();
  }

  std::pair<typename std::set<T*, Comparator<T>>::iterator, bool> insert(
    T* t) noexcept
  {
    if (std::set<T*, Comparator<T>>::size() >= deep_) {
      auto&& it = std::set<T*, Comparator<T>>::begin();
      if (*it)
        delete (*it);
      std::set<T*, Comparator<T>>::erase(it);
    }
    return std::set<T*, Comparator<T>>::insert(t);
  }

  typename std::set<T*, Comparator<T>>::iterator set_erase(
    typename std::set<T*, Comparator<T>>::iterator it) noexcept
  {
    if (*it)
      delete (*it);
    return std::set<T*, Comparator<T>>::erase(it);
  }

  void set_clone(const std::set<T*, Comparator<T>>* s) noexcept
  {
    set_clear();
    for (auto&& it = s->begin(); it != s->end(); ++it)
      if ((*it) != nullptr)
        std::set<T*, Comparator<T>>::insert((*it)->clone());
  }

  friend std::ostream& operator<<(std::ostream& o,
                                  const PointerSet<T, deep>& ps) noexcept
  {
    for (auto&& it = ps.begin(); it != ps.end(); ++it) {
      if (*it)
        o << "  " << *(*it) << ' ';
    }
    return o;
  }

  const T* front() const noexcept
  {
    if (std::set<T*, Comparator<T>>::empty())
      return nullptr;
    return (*(std::set<T*, Comparator<T>>::begin()));
  }

  const T* back() const noexcept
  {
    if (std::set<T*, Comparator<T>>::empty())
      return nullptr;
    return (*(std::set<T*, Comparator<T>>::rbegin()));
  }

protected:
  usize_t deep_ = 400;
};

#endif